1
2
3
4
5
6
7 package org.opensciencegrid.authz.service;
8
9 import java.util.StringTokenizer;
10 import java.util.ArrayList;
11 import java.util.Enumeration;
12 import java.io.IOException;
13 import java.io.BufferedReader;
14 import java.io.InputStreamReader;
15 import java.io.InputStream;
16 import java.io.FileInputStream;
17
18 import org.opensciencegrid.authz.common.GridId;
19 import org.opensciencegrid.authz.common.LocalId;
20
21 import org.apache.log4j.Category;
22
23 /***
24 * An example simple mapping service implementation that
25 * maps an incoming request based on
26 * /etc/grid-security/grid-mapfile
27 *
28 * @author G. Carcassi, M. Lorch
29 */
30
31
32
33 public class GRIDIdentityMappingServiceExampleImpl implements GRIDIdentityMappingService {
34
35 /*** Log4Java logger */
36 static Category log = Category.getInstance(GRIDIdentityMappingServiceExampleImpl.class.getName() );
37
38 /*** the standard Gridmap file location */
39 private static String STDMAPFILE = "/etc/grid-security/mapsvc-gridmap";
40
41 /*** the comment chars for the gridmap file */
42 private static final String COMMENT_CHARS = "#";
43
44 /*** the contents of the gridmap file, parsed at object instantiation */
45 private ArrayList mappings;
46
47 /*** internal format to hold gridmap entries */
48 private class MapEntry extends LocalId {
49 String subjectDN;
50 String fqan;
51 public void setSubjectDN(String s) {this.subjectDN = s;}
52 public String getSubjectDN() {return this.subjectDN;}
53 public void setFQAN(String f) {this.fqan = f;}
54 public String getFQAN() {return this.fqan;}
55 public String toString() {
56 String r = subjectDN+" "+getUserName()+" "+fqan+" "+getGroupName()+" ";
57 String[] gs = getSupplementalGroupNames();
58 if(gs!=null) {
59 for(int i=0;i<gs.length;i++)
60 r = r.concat(gs[i]+" ");
61 }
62 return r;
63 }
64 }
65
66 /*** initialization method parses gridmap file
67 * gridmap file location may be supplied via system property gridmap.file
68 */
69 public void init() {
70 String mapfile = System.getProperty("gridmap.file");
71 if(mapfile!=null)
72 log.debug("Parsing gridmap file from: "+mapfile);
73 else {
74 mapfile=STDMAPFILE;
75 log.debug("System property gridmap.file not available");
76 log.debug("Parsing gridmap file from standard location: "+mapfile);
77 }
78 try {
79 mappings = parseMappingFile(mapfile);
80 } catch (Exception e) {
81 log.error("Unable to parse gridmap file: "+e);
82 log.debug(e);
83 }
84 if(mappings==null || mappings.isEmpty()) log.debug("No gridmap entries were retrieved from gridmap file");
85 else log.debug("Gridmap table loaded: "+mappings);
86 }
87
88 /*** the main function called from the outside, please note that
89 * this is a test implementation
90 * the host name of the service for which a mapping is being
91 * requested is not checked, neither is the issuer of the VOMS
92 * attribute (fqan) considered
93 */
94 public LocalId mapCredentials(GridId gridID) {
95 log.debug("entered mapCredentials of mapping service");
96 MapEntry m;
97
98 if(mappings==null) init();
99
100 if(mappings==null) {
101 log.debug("mapping table is empty, returning null (deny)");
102 return null;
103 }
104
105 log.debug("attempting to find a mapping");
106
107 for(int i=0;i<mappings.size();i++) {
108 m = (MapEntry) mappings.get(i);
109 log.debug("comparing "+m.getSubjectDN()+" to "+gridID.getUserDN());
110 if (m.getSubjectDN().equals(gridID.getUserDN()) ) {
111 log.debug("found matching subject DN in gridmap table");
112
113 if(m.getFQAN()==null && gridID.getUserFQAN()==null) {
114 log.debug("no FQANS, returning mapping for local user: "+m.getUserName());
115 return (LocalId) m;
116 }
117
118 if(m.getFQAN()==null || gridID.getUserFQAN()==null) {
119 log.debug("FQANs don't match for this entry (null)");
120 continue;
121 }
122
123 if(m.getFQAN().equals(gridID.getUserFQAN())) {
124 log.debug("FQAN matches, returning mapping for local user: "+m.getUserName());
125 return (LocalId) m;
126 }
127 else {
128 log.debug("FQANs don't match for this entry");
129 }
130
131 }
132
133 }
134
135 log.debug("returning null, no valid mapping could be found");
136 return null;
137
138 }
139
140
141 /*** file parser function that returns an array of localID objects
142 * function is tuned for configurations with up to 50 entries
143 * it can parse standard GLOBUS gridmap files as well as gridmap
144 * files that also contain fqan, group, and supplemental group
145 * fields (in that order)
146 * It DOES NOT support multiple local user names (separated via a comma)
147 */
148 private ArrayList parseMappingFile(String mfile)
149 throws IOException {
150
151 InputStream in = null;
152 String line;
153 ArrayList mappings = new ArrayList(50);
154 MapEntry m;
155 QuotedStringTokenizer tokenizer;
156
157 in = new FileInputStream(mfile);
158
159 BufferedReader reader =
160 new BufferedReader(new InputStreamReader(in));
161
162 while( (line = reader.readLine()) != null) {
163 line = line.trim();
164 if ( (line.length() == 0) ||
165 ( line.substring(0, 1).indexOf(COMMENT_CHARS) != -1) ) {
166 continue;
167 }
168
169
170 m = new MapEntry();
171
172 tokenizer = new QuotedStringTokenizer(line);
173
174 if (tokenizer.hasMoreTokens()) {
175 m.setSubjectDN(tokenizer.nextToken());
176 } else {
177 throw new IOException("Subject DN not defined: " + line);
178 }
179 if (tokenizer.hasMoreTokens()) {
180 m.setUserName(tokenizer.nextToken());
181 } else {
182 throw new IOException("Local user name not defined: " + line);
183 }
184 if (tokenizer.hasMoreTokens()) {
185 m.setFQAN(tokenizer.nextToken());
186 }
187 if (tokenizer.hasMoreTokens()) {
188 m.setGroupName(tokenizer.nextToken());
189 }
190
191 if (tokenizer.hasMoreTokens()) {
192 ArrayList sg = new ArrayList(16);
193 while(tokenizer.hasMoreTokens() ) {
194 sg.add(tokenizer.nextToken());
195 }
196 if(!sg.isEmpty()) {
197 String [] sglist = new String[sg.size()];
198 for(int i=0;i<sg.size();i++) {
199 sglist[i]=(String)sg.get(i);
200 }
201 m.setSupplementalGroupNames(sglist);
202 }
203 }
204
205
206
207
208 mappings.add(m);
209
210 }
211
212
213 if (in != null) in.close();
214
215 return mappings;
216 }
217
218
219 }
220
221
222
223
224
225
226
227 class QuotedStringTokenizer implements Enumeration {
228
229 private int limit;
230 private int start;
231 private String str;
232
233 public QuotedStringTokenizer(String str) {
234 this.str = str;
235 start = 0;
236 limit = str.length();
237 }
238
239 public Object nextElement() {
240 return nextToken();
241 }
242
243 public String nextToken() {
244 while ((start < limit) && (str.charAt(start) <= ' ')) {
245 start++;
246 }
247
248 if (start == limit) return null;
249
250 StringBuffer buf = new StringBuffer(limit-start);
251 char ch;
252 char quote = str.charAt(start);
253 if (quote == '"' || quote == '\'') {
254 start++;
255 for (int i=start;i<limit;i++) {
256 ch = str.charAt(i);
257 start++;
258 if (ch == quote) {
259 break;
260 } else if (ch == '//') {
261 buf.append( str.charAt(++i) );
262 start++;
263 } else {
264 buf.append(ch);
265 }
266 }
267 return buf.toString();
268 } else {
269 for (int i=start;i<limit;i++) {
270 ch = str.charAt(i);
271 start++;
272 if (ch == ' ') {
273 break;
274 } else {
275 buf.append(ch);
276 }
277 }
278 }
279
280 return buf.toString();
281 }
282
283 public boolean hasMoreElements() {
284 return hasMoreTokens();
285 }
286
287 public boolean hasMoreTokens() {
288 while ((start < limit) && (str.charAt(start) <= ' ')) {
289 start++;
290 }
291
292 return (start != limit);
293 }
294
295 public int countTokens() {
296 int localStart = start;
297 int i = 0;
298 while( nextToken() != null ) {
299 i++;
300 }
301 start = localStart;
302 return i;
303 }
304
305 }
306
307